#include "xfunc.widget.h"
#include "misc.h"
#include "DoubleSlider.h"
#include <QtWidgets/QtWidgets>

struct XfuncWidgetPriv {
	XfuncWidget* xfuncw;

	double xmin, xmax;
	double xvals[2];
	int xvalsi[2];
	QLineEdit* spins[2];
	QSlider* sliders[2];
	DoubleSlider* dslider;
	QToolButton* toolBtn;
	QPushButton* btnSetRange;
	QPushButton* btnResetRange;
	XfuncWidgetPriv(XfuncWidget* par):
		xfuncw{par}, xmin{0.0}, xmax{1.0},
		xvals{0.0, 1.0},
		xvalsi{0, 10000}
	{
		toolBtn=new QToolButton{par};
		auto validator=new QDoubleValidator{par};
		auto wid=new QWidget{nullptr};
		for(int i=0; i<2; i++) {
			spins[i]=new QLineEdit{QString::number(xvals[i], 'g', 3), wid};
			spins[i]->setValidator(validator);
			spins[i]->setMinimumWidth(64);
			spins[i]->setMaximumWidth(64);
			spins[i]->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
		}
		QObject::connect(spins[0], &QLineEdit::editingFinished, xfuncw, &XfuncWidget::spin0Changed);
		QObject::connect(spins[1], &QLineEdit::editingFinished, xfuncw, &XfuncWidget::spin1Changed);
		for(int i=0; i<2; i++) {
			sliders[i]=new QSlider{Qt::Horizontal, par};
			sliders[i]->setTickPosition(QSlider::NoTicks);
			sliders[i]->setMaximum(10000);
			sliders[i]->setMinimum(0);
			sliders[i]->setValue(xvalsi[i]);
			sliders[i]->setMinimumWidth(128);
			sliders[i]->hide();
		}
		
		dslider = new DoubleSlider(par);
		QObject::connect(sliders[0], &QSlider::valueChanged, xfuncw, &XfuncWidget::slider0Changed);
		QObject::connect(sliders[1], &QSlider::valueChanged, xfuncw, &XfuncWidget::slider1Changed);
		QObject::connect(dslider, &DoubleSlider::slider0Change, xfuncw, &XfuncWidget::slider0Changed);
		QObject::connect(dslider, &DoubleSlider::slider1Change, xfuncw, &XfuncWidget::slider1Changed);

		btnSetRange=new QPushButton{"&Shrink", wid};
		btnSetRange->setEnabled(false);
		QObject::connect(btnSetRange, &QPushButton::clicked, xfuncw, &XfuncWidget::setRange);
		btnResetRange=new QPushButton{"&Reset", wid};
		btnResetRange->setEnabled(false);
		QObject::connect(btnResetRange, &QPushButton::clicked, xfuncw, &XfuncWidget::resetRange);

		auto layout=new QGridLayout{wid};
		layout->addWidget(spins[0], 1, 1, 1, 1);
		layout->addWidget(spins[1], 0, 1, 1, 1);
		layout->addWidget(btnSetRange, 0, 2, 1, 1);
		layout->addWidget(btnResetRange, 1, 2, 1, 1);
		layout->setColumnStretch(0, 1);

		layout=new QGridLayout{par};
		//layout->addWidget(sliders[0], 1, 0, 1, 1);
		//layout->addWidget(sliders[1], 0, 0, 1, 1);
		layout->addWidget(dslider, 0, 0, 1, 1);
		layout->addWidget(toolBtn, 0, 1, 2, 1);


		auto act=new QWidgetAction{par};
		act->setDefaultWidget(wid);
		auto btnMenu=new QMenu("Transfer function", par);
		btnMenu->addAction(act);

		toolBtn->setMenu(btnMenu);
		toolBtn->setPopupMode(QToolButton::InstantPopup);
	}
	~XfuncWidgetPriv() {
	}
	template<int i> void setSliderValue(int v) {
		if(v>10000)
			v=10000;
		if(v<0)
			v=0;
		if(xvalsi[i]!=v) {
			xvalsi[i]=v;
			auto oldv=sliders[i]->blockSignals(true);
			sliders[i]->setValue(v);
			sliders[i]->blockSignals(oldv);
		}
	}
	template<int i> void setSpinValue(double v) {
		if(v>xmax)
			v=xmax;
		if(v<xmin)
			v=xmin;
		if(xvals[i]!=v) {
			xvals[i]=v;
			auto oldv=spins[i]->blockSignals(true);
			spins[i]->setText(QString::number(v, 'g', 3));
			spins[i]->blockSignals(oldv);
		}
	}
	void spin0Changed() {
		auto x=spins[0]->text().toDouble();
		if(x==xvals[0])
			return;
		xvals[0]=x;
		if(x<xmin) {
			xmin=x;
			setSliderValue<1>(lrint(10000*(xvals[1]-xmin)/(xmax-xmin)));
		} else if(x>xvals[1]) {
			if(x>xmax)
				xmax=x;
			setSpinValue<1>(x);
			setSliderValue<1>(lrint(10000*(xvals[1]-xmin)/(xmax-xmin)));
		}
		setSliderValue<0>(lrint(10000*(xvals[0]-xmin)/(xmax-xmin)));
		updateButtons();
		Q_EMIT xfuncw->changed();
	}
	void spin1Changed() {
		auto x=spins[1]->text().toDouble();
		if(x==xvals[1])
			return;
		xvals[1]=x;
		if(x>xmax) {
			xmax=x;
			setSliderValue<0>(lrint(10000*(xvals[0]-xmin)/(xmax-xmin)));
		} else if(x<xvals[0]) {
			if(x<xmin)
				xmin=x;
			setSpinValue<0>(x);
			setSliderValue<0>(lrint(10000*(xvals[0]-xmin)/(xmax-xmin)));
		}
		setSliderValue<1>(lrint(10000*(xvals[1]-xmin)/(xmax-xmin)));
		updateButtons();
		Q_EMIT xfuncw->changed();
	}
	void slider0Changed(int x) {
		if(xvalsi[0]==x)
			return;
		xvalsi[0]=x;
		setSpinValue<0>(x*(xmax-xmin)/10000+xmin);
		if(xvals[0]>xvals[1]) {
			setSpinValue<1>(xvals[0]);
			setSliderValue<1>(x);
		}
		updateButtons();
		Q_EMIT xfuncw->changed();
	}
	void slider1Changed(int x) {
		if(xvalsi[1]==x)
			return;
		xvalsi[1]=x;
		setSpinValue<1>(x*(xmax-xmin)/10000+xmin);
		if(xvals[1]<xvals[0]) {
			setSpinValue<0>(xvals[1]);
			setSliderValue<0>(x);
		}
		updateButtons();
		Q_EMIT xfuncw->changed();
	}
	void setRange() {
		if(xvals[0]>=xvals[1])
			return;
		xmin=xvals[0];
		xmax=xvals[1];
		setSliderValue<0>(0);
		setSliderValue<1>(10000);
		updateButtons();
		dslider->reset(0,1);
		dslider->update();
	}
	void resetRange() {
		xmin=0;
		xmax=1;
		setSpinValue<0>(0);
		setSpinValue<1>(1);
		setSliderValue<0>(0);
		setSliderValue<1>(10000);
		updateButtons();
		Q_EMIT xfuncw->changed();
		dslider->reset(0, 1);
		dslider->update();
	}
	void updateButtons() {
		btnSetRange->setEnabled(xvals[0]<xvals[1] && (xmin!=xvals[0] || xmax!=xvals[1]));
		btnResetRange->setEnabled(xmin!=0 || xmax!=1 || xvals[0]!=0 || xvals[1]!=1);
	}
	void setPositions(double x0, double x1) {
		if(x0>x1)
			return;
		if(x0<xmin)
			xmin=x0;
		if(x1>xmax)
			xmax=x1;
		setSpinValue<0>(x0);
		setSpinValue<1>(x1);
		setSliderValue<0>(lrint(10000*(xvals[0]-xmin)/(xmax-xmin)));
		setSliderValue<1>(lrint(10000*(xvals[1]-xmin)/(xmax-xmin)));
		updateButtons();
		Q_EMIT xfuncw->changed();
	}
};

XfuncWidget::XfuncWidget(QWidget* par):
	QWidget{par}, priv{new XfuncWidgetPriv{this}}
{
}
XfuncWidget::~XfuncWidget() {
	delete priv;
}

void XfuncWidget::setPositions(double x0, double x1) {
	priv->setPositions(x0, x1);
}
std::pair<double, double> XfuncWidget::positions() const {
	return {priv->xvals[0], priv->xvals[1]};
}
void XfuncWidget::setRange() {
	priv->setRange();
}
void XfuncWidget::resetRange() {
	priv->resetRange();
}
void XfuncWidget::spin0Changed() {
	return priv->spin0Changed();
}
void XfuncWidget::spin1Changed() {
	return priv->spin1Changed();
}
void XfuncWidget::slider0Changed(int x) {
	return priv->slider0Changed(x);
}
void XfuncWidget::slider1Changed(int x) {
	return priv->slider1Changed(x);
}

